/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package idea.sfo.utils;


import designersfo.Root;
import idea.model.dbmanager.QueryManager;
import idea.model.dbmanager.SfoEnum;
import idea.model.qnrlibrary.QuestionTitle;
import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Iterator;
import java.util.List;
import javax.swing.ImageIcon;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.codec.binary.Base64;
import org.openide.util.Exceptions;
import sfoutils.SFOUtils;


/**
 * The SfoDocumentManager class handles reading & writing SFO objects to & from
 * the database.
 *
 * @author renke002
 */
public class SfoDocumentManager
{

    /**
     * number of non ASCII chars at start of SFO
     */
    private final int BAD_CHAR_COUNT = 2;
    /**
     * Questionnaire Identification Number.
     */
    private String questId = null;
    /**
     * Content of the SFO object represented as a String.
     */
    private String sfoContent;
    /**

* ER add JAXB context to set up marshalers
     */
    private JAXBContext jc = null;
    /**
     * the non ASCII characters that begin an SFO document
     */
    private char[] savedNonAscii = new char[ BAD_CHAR_COUNT ];


    /**
     *
     */
    public SfoDocumentManager( String sfoFileName )
    {
        /**
         * Ed add set up the JAXB Context
         */
        try
        {
            jc = JAXBContext.newInstance( "designersfo" );
        }
        catch ( Exception e )
        {
            Exceptions.printStackTrace( e );
        }

        this.sfoContent = this.getSfoString( SfoEnum.LAYOUT , questId );
    }


    /**
     *
     * @param questId
     * @param sfoType
     */
    public SfoDocumentManager( String questId , SfoEnum sfoType )
    {
        this.questId = questId;

        try
        {
            jc = JAXBContext.newInstance( "designersfo" );
        }
        catch ( Exception e )
        {
            Exceptions.printStackTrace( e );
        }
        this.sfoContent = this.getSfoString( sfoType , questId );
    }


    /**
     *
     * @param sfo
     * @param questId
     * @param savedNonAscii
     * @throws JAXBException
     */
    public SfoDocumentManager( String sfo , String questId , char[] savedNonAscii ) throws JAXBException
    {
        try
        {
            jc = JAXBContext.newInstance( "designersfo" );
        }
        catch ( Exception e )
        {
            Exceptions.printStackTrace( e );
        }
        this.sfoContent = sfo;
        this.questId = questId;
        this.savedNonAscii = savedNonAscii;
    }


    /**
     *
     * @return a reference to the SFO's XML Document Root
     */
    public Root getDocRoot()
    {
        try
        {
            return this.getSfoRoot();
        }
        catch ( JAXBException ex )
        {
            Exceptions.printStackTrace( ex );
        }
        return null;
    }


    /**
     * Gets the SFO xml file for the given quest ID.
     *
     * @param questId -- the Question id whose SFO we are requesting
     * @return a String which is the requested SFO
     */
    private String getSfoString( SfoEnum sfoType , String questId )
    {
        final int ARRAY_SIZE = 40000;
        //final int BAD_CHAR_COUNT = 2;
        String sfoString = null;

        // go get the Clob from the DB
        QueryManager qm = new QueryManager();
        List<QuestionTitle> qts = QueryManager.getQT( questId );
        QuestionTitle qt = qts.get( 0 ); // should only be one

        long clobLength = qm.getClobFromDB( sfoType , qt );
        if ( clobLength > 0 )
        {
            String clobStr = qm.getClobStr();
            System.out.println( "Clob length is " + clobLength + ", Clob is " + clobStr.substring( 0 , 50 ) );

            // decrypt it
            SFOUtils su = new SFOUtils();
            su.setB64( new Base64() );
            su.setDecoded( su.getB64().decode( clobStr ) );
            String decodedStr = new String( su.getDecoded() );
            System.out.println( sfoType.toString() + " decoded compressed len " + decodedStr.length() );

            // uncompress it
            int reslen = su.inflate( su.getDecoded() , ARRAY_SIZE ); // result is put into su.output
            sfoString = su.getOutput();

            int len = sfoString.length();
            System.out.println( "SFO string length is " + len );

            // REMOVE and save off the two nonASCII characters at the beginning of the sfo string, and be sure two bogus chars aren't added at the end.
            char[] sfoChars = sfoString.toCharArray();
            char[] sfoOut = new char[ sfoChars.length - BAD_CHAR_COUNT ];
            int j = 0;
            for ( int i = 0; i < sfoChars.length; i++ )
            {
                if ( i < BAD_CHAR_COUNT )
                {
                    this.savedNonAscii[i] = sfoChars[i];
                    continue;
                }
                char c = sfoChars[i];
                sfoOut[j++] = c;
            }

            sfoString = new String( sfoOut );

            System.out.println( "--------------------------------------" );
            System.out.println( sfoType.toString() + "\n/" + su.homelyPrint( sfoString ) + "/" );
        }
        else
        {
            Exceptions.printStackTrace( new Exception( "ERROR: No " + sfoType.name() + " in custom_content_layouts for given Question Title ID." ) );
        }

        return sfoString;
    }


    /**
     *
     * @param sfoType
     * @throws JAXBException
     */
    private Root getSfoRoot() throws JAXBException
    {
        String sfoString = "<?xml version=\"1.0\"?>" + this.sfoContent;

        Unmarshaller um = jc.createUnmarshaller();

        return (Root) um.unmarshal( new StreamSource( new StringReader( sfoString ) ) );
    }


    /**
     * more generic, unmarshalls (JAXB style) a given SFO xml file.
     *
     * @param sfoFile
     * @return the root object containing the entire tree.
     * @throws JAXBException
     */
    public Root loadSFOFile( String sfoFile ) throws JAXBException
    {
        Unmarshaller um = this.jc.createUnmarshaller();
        Root root = (Root) um.unmarshal( new File( sfoFile ) );
        return root;
    }


    /**
     * Save a compressed, encrypted SFO string to the DB Marshall the DocRoot to
     * a string.
     *
     * @throws JAXBException
     */
    public void saveSFO() throws JAXBException
    {
        this.saveSFO( this.getDocRoot() );
    }


    /**
     *
     * @param sfoDocRoot
     * @throws JAXBException
     */
    public void saveSFO( Root sfoDocRoot ) throws JAXBException
    {
        boolean testing = true;
        // get docRoot ready for DB
        StringWriter writer = new StringWriter();

        String questID = questId;
        final int COMP_BYTE_LEN = 30000;

        //byte[] compressed = new byte[ 2000 ];
        byte[] compressed = new byte[ COMP_BYTE_LEN ];
        String encodedStr;

        Marshaller m = jc.createMarshaller();
        m.marshal( sfoDocRoot , writer );
        String sfo = writer.toString();
        sfo = sfo.replaceFirst( "\\<\\?.*\\?\\>" , "" ); // First remove XML header
        int len = sfo.length();
        System.out.println( "SFO to save, len " + len + ", /" + sfo + "/" );
        if ( savedNonAscii != null )
        {
            // Add the two saved off nonAscii characters
            StringBuilder sb = new StringBuilder();
            for ( int i = 0; i < savedNonAscii.length; i++ )
            {
                sb = sb.append( savedNonAscii[i] );
            }
            sb = sb.append( sfo );
            sfo = sb.toString();
        }
        int sfolen = sfo.length();
        System.out.println( "Root string for questID " + questId + ": (len " + sfolen + ") /" + sfo + "/" );
        SFOUtils sfoUtil = new SFOUtils();
        int compLen = sfoUtil.deflate( sfo , compressed );
        System.out.println( "Compressed len is " + compLen );

//        if ( testing )
//        {
//            int reslen = sfoUtil.inflate( compressed , COMP_BYTE_LEN );
//            System.out.println( "Res len is " + reslen );
//            String testSfo = sfoUtil.getOutput();
//            int testSfoLen = testSfo.length();
//            System.out.println( "Test SFO len is " + testSfoLen + ", /" + testSfo + "/" );
//        }

        encodedStr = Base64.encodeBase64String( compressed );
        System.out.println( "compressed and encoded (" + encodedStr.length() + " bytes):" );
        System.out.println( "to store in DB: /" + encodedStr + "/" );

//        if ( testing )
//        {
//            sfoUtil.setB64( new Base64() );
//            sfoUtil.setDecoded( sfoUtil.getB64().decode( encodedStr ) );
//            System.out.println( "decoded compressed len : " + sfoUtil.getDecoded().toString().length() + ", /" + sfoUtil.getDecoded().toString() + "/" );
//        }

        // go get the QuestionTitle for this questId
        List<QuestionTitle> qts = QueryManager.getQT( questID );
        Iterator<QuestionTitle> qtit = qts.iterator();
        QuestionTitle qt = null;
        while ( qtit.hasNext() )
        {
            qt = qtit.next();
            break;  // there will only be one
        }

        QueryManager.updateSfoClob( SfoEnum.DESIGNER , encodedStr , qt );
    }
    
    /**
     * This returns an image of the question jpeg for the current questID.
     * The jpeg is stored in the custom_content_layouts table.
     * @return ImageIcon of the jpeg
     */
    public ImageIcon getJpegImage()
    {
        ImageIcon image = null;
        if ( questId != null )
        {
            List<QuestionTitle> qts = QueryManager.getQT( questId );
            QuestionTitle qt = null;
            Iterator<QuestionTitle> qtit = qts.iterator();
            while ( qtit.hasNext() )
            {
                qt = qtit.next();
                break;
            }
            image = QueryManager.getJpegFromDB( qt );
        }
        return image;
    }

}

